package com.acme.bankapp.service.bank;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

import com.acme.bankapp.domain.bank.Account;
import com.acme.bankapp.domain.bank.AccountType;
import com.acme.bankapp.domain.bank.Bank;
import com.acme.bankapp.domain.bank.CheckingAccount;
import com.acme.bankapp.domain.bank.Client;
import com.acme.bankapp.domain.bank.Gender;
import com.acme.bankapp.domain.bank.SavingsAccount;
import com.acme.bankapp.exceptions.ClientExistsException;

public class BankDataLoaderService {
	static final String FEED_REC_DELIMITER = ";";
	static final String FEED_KV_DELIMITER = "=";
	
	static final String ACCOUNT_TYPE_CHECKING = "c";
	static final String ACCOUNT_TYPE_SAVING = "s";
	
	static final String GENDER_FEMALE = "f";
	static final String GENDER_MALE = "m";
	
	static final String KEY_ACCOUNTTYPE = "accounttype";
	static final String KEY_BALANCE = "balance";
	static final String KEY_OVERDRAFT = "overdraft";
	static final String KEY_NAME = "name";
	static final String KEY_GENDER = "gender";
	
	static final String MES_INVALID_ACCOUNT_TYPE = "Account type is invalid";
	static final String MES_INVALID_GENDER = "Gender is invalid";
	static final String MES_INVALID_FEED_STRUCTURE = "client data has invalid structure";
	
	public static void parseFeedFile(Bank bank, String filename) throws FileNotFoundException, ParseFeedFileException, ArrayIndexOutOfBoundsException, ClientExistsException {		
		BufferedReader reader = new BufferedReader(new FileReader(filename));
		String s;
		try {
			while ((s = reader.readLine()) != null) {
				try {
					Client client = parseClient(s);
					if (client != null)
						BankService.addClient(bank, client);
				} catch (ParseFeedFileException e) {
					System.out.println(e.getMessage());
				}
			}
		}
		catch (IOException  ex) {
			throw new ParseFeedFileException(MES_INVALID_FEED_STRUCTURE);
		} finally {		
			try {
				reader.close();
			} catch (IOException e) {
				throw new ParseFeedFileException(MES_INVALID_FEED_STRUCTURE);				
			}
		}
	}
	
	public static Client parseClient(String s) throws ParseFeedFileException {
		Client client;
		Scanner scanner = new Scanner(s);
		scanner.useDelimiter(FEED_REC_DELIMITER);
		
		try {			
			AccountType type = parseAccountType(scanner.next());
			double balance;
			double overdraft;
			Account account = null;
			
			switch (type) {
			case SAVING:
				balance = parseBalance(scanner.next());
				account = new SavingsAccount(balance);
				break;
			case CHECKING:
				balance = parseBalance(scanner.next());
				overdraft = parseOverdraft(scanner.next());
				account = new CheckingAccount(balance, overdraft);
				break;
			}
			
			String name = parseName(scanner.next());
			Gender gender = parseGender(scanner.next());
			
			client = new Client(name, gender, account);
			
		} finally {
			scanner.close();
		}
		
		return client;
	}
	
	private static Gender parseGender(String s) throws ParseFeedFileException {
		String value = getValueOfPair(s, KEY_GENDER);
		
		switch (value) {
			case GENDER_FEMALE:
				return Gender.FEMALE;
			case GENDER_MALE:
				return Gender.MALE;
				default:
					throw new ParseFeedFileException(MES_INVALID_GENDER + ". key = " + KEY_GENDER + ", value = " + value);
		}
	}

	private static String parseName(String s) throws ParseFeedFileException {
		return getValueOfPair(s, KEY_NAME);
	}

	static double parseBalance(String s) throws ParseFeedFileException {
		String value = getValueOfPair(s, KEY_BALANCE);
		
		double balance = 0;
		try {
			balance = Double.parseDouble(value);
		} catch (NumberFormatException ex) {
			throw new ParseFeedFileException(MES_INVALID_FEED_STRUCTURE + ". key = " + KEY_BALANCE + ", value = " + value);
		}
		
		return balance;
	}

	static double parseOverdraft(String s) throws ParseFeedFileException {
		String value = getValueOfPair(s, KEY_OVERDRAFT);
		
		double overdraft = 0;
		try {
			overdraft = Double.parseDouble(value);
		} catch (NumberFormatException ex) {
			throw new ParseFeedFileException(MES_INVALID_FEED_STRUCTURE + ". key = " + KEY_OVERDRAFT + ", value = " + value);
		}
		
		return overdraft;
	}

	static AccountType parseAccountType(String s) throws ParseFeedFileException {
		String value = getValueOfPair(s, KEY_ACCOUNTTYPE);
		
		switch (value) {
			case ACCOUNT_TYPE_CHECKING:
				return AccountType.CHECKING;
			case ACCOUNT_TYPE_SAVING:
				return AccountType.SAVING;
				default:
					throw new ParseFeedFileException(MES_INVALID_ACCOUNT_TYPE + ". key = " + KEY_ACCOUNTTYPE + ", value = " + value);
		}
	}

	static String getValueOfPair(String s, String key) throws ParseFeedFileException {
		String[] kv = s.split(FEED_KV_DELIMITER);
		if (kv.length != 2)
			throw new ParseFeedFileException(MES_INVALID_FEED_STRUCTURE + ". key = " + key);

		if (!kv[0].equals(key))
			throw new ParseFeedFileException(MES_INVALID_FEED_STRUCTURE + ". key = " + key);
				
		return kv[1];		
	}
}
